/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2025-2025. All rights reserved.
 * MemCache_Hybrid is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
*/
#include "mmc.h"

#include "mmc_client.h"
#include "mmc_common_includes.h"
#include "mmc_configuration.h"
#include "mmc_def.h"
#include "mmc_env.h"
#include "mmc_service.h"

using namespace ock::mmc;
static mmc_local_service_t g_localService;

static std::mutex gMmcMutex;
static bool mmcInit = false;

MMC_API int32_t mmc_init(const mmc_init_config *config)
{
    MMC_VALIDATE_RETURN(config != nullptr, "config is null", MMC_INVALID_PARAM);
    MMC_VALIDATE_RETURN(config->deviceId <= MAX_DEVICE_ID,
        "Invalid param deviceId: " << config->deviceId, MMC_INVALID_PARAM);
    std::lock_guard<std::mutex> lock(gMmcMutex);
    if (mmcInit) {
        MMC_LOG_INFO("mmc is already init");
        return MMC_OK;
    }

    MMC_VALIDATE_RETURN(!MMC_LOCAL_CONF_PATH.empty(), "MMC_LOCAL_CONFIG_PATH is not set", MMC_INVALID_PARAM);

    ClientConfig configManager{};

    MMC_VALIDATE_RETURN(configManager.LoadFromFile(MMC_LOCAL_CONF_PATH), "Failed to load client config", MMC_ERROR);

    const std::vector<std::string> validationError = configManager.ValidateConf();
    if (!validationError.empty()) {
        MMC_LOG_ERROR("Wrong configuration in file <" << MMC_LOCAL_CONF_PATH << ">, because of following mistakes:");
        for (auto &item : validationError) {
            MMC_LOG_ERROR(item);
        }
        return MMC_INVALID_PARAM;
    }

    mmc_local_service_config_t localServiceConfig{};
    localServiceConfig.flags = 0;
    localServiceConfig.deviceId = config->deviceId;
    configManager.GetLocalServiceConfig(localServiceConfig);
    MMC_RETURN_ERROR(ock::mmc::MmcOutLogger::Instance().SetLogLevel(static_cast<LogLevel>(localServiceConfig.logLevel)),
                     "failed to set log level " << localServiceConfig.logLevel);
    if (localServiceConfig.logFunc != nullptr) {
        MmcOutLogger::Instance().SetExternalLogFunction(localServiceConfig.logFunc);
    }

    MMC_VALIDATE_RETURN(configManager.ValidateLocalServiceConfig(localServiceConfig) == MMC_OK,
        "Invalid local service config", MMC_INVALID_PARAM);
    g_localService = mmcs_local_service_start(&localServiceConfig);
    MMC_VALIDATE_RETURN(g_localService != nullptr, "failed to create or start local service", MMC_ERROR);

    mmc_client_config_t clientConfig{};
    configManager.GetClientConfig(clientConfig);
    auto ret = mmcc_init(&clientConfig);
    if (ret != MMC_OK) {
        MMC_LOG_ERROR("mmcc init failed, ret:" << ret);
        mmcs_local_service_stop(g_localService);
        g_localService = nullptr;
        return ret;
    }
    mmcInit = true;
    return ret;
}

MMC_API int32_t mmc_set_extern_logger(void (*func)(int level, const char *msg))
{
    if (func == nullptr) {
        return MMC_INVALID_PARAM;
    }
    ock::mmc::MmcOutLogger::Instance().SetExternalLogFunction(func);
    return MMC_OK;
}

MMC_API int32_t mmc_set_log_level(int level)
{
    if (level < DEBUG_LEVEL || level >= BUTT_LEVEL) {
        return MMC_INVALID_PARAM;
    }
    ock::mmc::MmcOutLogger::Instance().SetLogLevel(static_cast<LogLevel>(level));
    return MMC_OK;
}

MMC_API void mmc_uninit()
{
    std::lock_guard<std::mutex> lock(gMmcMutex);
    if (!mmcInit) {
        MMC_LOG_INFO("mmc is not init");
        return;
    }

    if (g_localService != nullptr) {
        mmcs_local_service_stop(g_localService);
        g_localService = nullptr;
    }
    mmcc_uninit();
    mmcInit = false;
}